﻿using BitShuva.Common;
using BitShuva.Models;
using Raven.Client;
using Raven.Client.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web.Http;

namespace BitShuva.Controllers
{
    [RoutePrefix("api/albums")]
    public class AlbumsController : UserContextController
    {
        /// <summary>
        /// Uploads the album art for a song. The album art will be applied to all songs matching the artist and album.
        /// </summary>
        /// <param name="address">The HTTP address where the album art can be fetched. This is expected to be a temporary address created by FilePickr.</param>
        /// <param name="fileName">The file name. Used for extracting the extension.</param>
        /// <param name="artist">The artist this album art applies to.</param>
        /// <param name="album">The name of the album this album art applies to.</param>
        [HttpPost]
        [Route("uploadArt")]
        public async Task<Album> UploadAlbumArt(string address, string fileName, string artist, string album)
        {
            var downloader = new System.Net.WebClient();
            var unescapedFileName = Uri.UnescapeDataString(artist) + " - " + Uri.UnescapeDataString(album) + Path.GetExtension(fileName);

            var albumArtUri = await CdnManager.UploadAlbumArtToCdn(new Uri(address), artist, album, Path.GetExtension(fileName));
            var existingAlbum = await this.Session
                .Query<Album>()
                .FirstOrDefaultAsync(a => a.Artist == artist && a.Name == album);
            if (existingAlbum != null)
            {
                existingAlbum.AlbumArtUri = albumArtUri;
            }
            else
            {
                existingAlbum = new Album
                {
                    Name = album,
                    Artist = artist,
                    AlbumArtUri = albumArtUri
                };
                await this.Session.StoreAsync(existingAlbum);
            }

            // Update the songs on this album.
            var songsOnAlbum = await this.Session
                .Query<Song>()
                .Where(s => s.Artist == artist && s.Album == album)
                .ToListAsync();
            songsOnAlbum.ForEach(s => s.AlbumArtUri = albumArtUri);

            return existingAlbum;
        }

        /// <summary>
        /// Find album art with the specified artist and album name.
        /// </summary>
        /// <param name="songId">The ID of the song we're checking for.</param>
        /// <param name="artist">The artist name.</param>
        /// <param name="album">The album.</param>
        /// <returns>An Album-like object containing the ID of the song.</returns>
        [Route("art/{songId}/{artist}/{album}")]
        public async Task<dynamic> GetAlbumArt(string songId, string artist, string album)
        {
            var existingAlbum = await this.Session
                .Query<Album>()
                .FirstOrDefaultAsync(a => a.Artist == artist && a.Name == album);
            return new
            {
                SongId = songId,
                Artist = Match.Value(existingAlbum).PropertyOrDefault(a => a.Artist),
                Name = Match.Value(existingAlbum).PropertyOrDefault(a => a.Name),
                AlbumArtUri = Match.Value(existingAlbum).PropertyOrDefault(a => a.AlbumArtUri)
            };
        }

        [HttpGet]
        [Route("art/test")]
        public async Task<HttpResponseMessage> Test(string songId)
        {
            var existingSong = await this.Session
                .LoadAsync<Song>(songId);
            var response = new HttpResponseMessage();
            using (var webClient = new WebClient())
            {
                var bytes = await webClient.DownloadDataTaskAsync(existingSong.AlbumArtUri);
                response.Content = new StreamContent(new MemoryStream(bytes)); // this file stream will be closed by lower layers of web api for you once the response is completed.
                response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");

                return response;
            }
        }

        /// <summary>
        /// Streams an image from another domain through our domain. 
        /// Needed for client-side canvas rendering of images on other domains (e.g. on our media CDN.)
        /// For example, when upload a new album, we use this URL to draw an image to a canvas in order to extract prominent colors from the album art.
        /// </summary>
        /// <param name="imageUrl"></param>
        /// <returns></returns>
        [HttpGet]
        [Route("art/imageondomain")]
        public async Task<HttpResponseMessage> ImageOnDomain(string imageUrl)
        {
            var response = new HttpResponseMessage();
            using (var webClient = new WebClient())
            {
                var bytes = await webClient.DownloadDataTaskAsync(imageUrl);
                response.Content = new StreamContent(new MemoryStream(bytes)); // this file stream will be closed by lower layers of web api for you once the response is completed.
                response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
                return response;
            }
        }

        [HttpPost]
        [Route("upload")]
        public async Task<string> Upload(AlbumUpload album)
        {
            // Put the album art on the CDN.
            var albumArtUriCdn = await CdnManager.UploadAlbumArtToCdn(new Uri(album.AlbumArtUri), album.Artist, album.Name, ".jpg");

            // Put all the songs on the CDN.
            var songNumber = 1;
            foreach (var albumSong in album.Songs)
            {
                var songUriCdn = await CdnManager.UploadMp3ToCdn(albumSong.Address, album.Artist, album.Name, albumSong.FileName);
                var song = new Song
                {
                    Album = album.Name,
                    Artist = album.Artist,
                    AlbumArtUri = albumArtUriCdn,
                    CommunityRankStanding = CommunityRankStanding.Normal,
                    Genres = album.Genres.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(),
                    Name = albumSong.FileName,
                    Number = songNumber,
                    PurchaseUri = album.PurchaseUrl,
                    UploadDate = DateTime.UtcNow,
                    Uri = songUriCdn
                };
                await this.Session.StoreAsync(song);
                songNumber++;
            }

            // Store the new album.
            var newAlbum = new Album
            {
                AlbumArtUri = albumArtUriCdn,
                Artist = album.Artist,
                BackgroundColor = album.BackColor,
                ForegroundColor = album.ForeColor,
                MutedColor = album.MutedColor,
                Name = album.Name,
                TextShadowColor = album.TextShadowColor
            };
            await this.Session.StoreAsync(newAlbum);

            await this.Session.SaveChangesAsync();
            return newAlbum.Id;
        }

        [HttpGet]
        [Route("art/get")]
        public async Task<HttpResponseMessage> GetAlbumArt(string artist, string album)
        {
            var redirectUri = default(Uri);
            var existingAlbum = await this.Session.Query<Album>()
                .FirstOrDefaultAsync(a => a.Artist == artist && a.Name == album);
            if (existingAlbum != null && existingAlbum.AlbumArtUri != null)
            {
                redirectUri = existingAlbum.AlbumArtUri;
            }
            else
            {
                // We don't have an album for this. See if we have a matching song.
                var matchingSong = await this.Session.Query<Song>().FirstOrDefaultAsync(s => s.Album == album && s.Artist == artist);
                if (matchingSong != null && matchingSong.AlbumArtUri != null)
                {
                    redirectUri = matchingSong.AlbumArtUri;
                }
            }

            if (redirectUri != null)
            {
                var response = Request.CreateResponse(HttpStatusCode.Moved);
                response.Headers.Location = redirectUri;
                return response;
            }

            throw new Exception("Unable to find any matching album art for " + artist + " - " + album);
        }
    }
}
